/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

#include <thrift/Thrift.h>
#include <cstring>
#include <cstdlib>
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
#include <stdarg.h>
#include <stdio.h>

namespace apache {
namespace thrift {

int thrift_vsnprintf(char *buf, size_t buflen, const char *format, va_list ap)
{
  int r;
  if (!buflen) {
    return 0;
  }
#ifdef _MSC_VER
  r = _vsnprintf_s(buf, buflen, buflen, format, ap);
  if (r < 0) {
    r = _vscprintf(format, ap);
  }
#else
  r = vsnprintf(buf, buflen, format, ap);
#endif
  buf[buflen - 1] = '\0';
  return r;
}

int thrift_snprintf(char *buf, size_t buflen, const char *format, ...)
{
  int r;
  va_list ap;
  va_start(ap, format);
  r = thrift_vsnprintf(buf, buflen, format, ap);
  va_end(ap);
  return r;
}

static void _warn_helper(int severity, const char *errstr, const char *fmt, va_list ap)
{
  char buf[1024];
  size_t len;

  if (fmt != NULL) {
    thrift_vsnprintf(buf, sizeof(buf), fmt, ap);
  }
  else {
    buf[0] = '\0';
  }
  if (errstr) {
    len = strlen(buf);
    if (len < sizeof(buf)-3) {
      thrift_snprintf(buf + len, sizeof(buf)-len, ": %s", errstr);
    }
  }

  TLogging::log(severity, buf);
}

void
TLogging::log_err(const char *fmt, ...)
{
  va_list ap;
  va_start(ap, fmt);
  _warn_helper(THRIFT_LOG_ERR, NULL, fmt, ap);
  va_end(ap);
}

void TLogging::log_warn(const char *fmt, ...)
{
  va_list ap;

  va_start(ap, fmt);
  _warn_helper(THRIFT_LOG_WARN, NULL, fmt, ap);
  va_end(ap);
}

void TLogging::log_info(const char *fmt, ...)
{
  va_list ap;

  va_start(ap, fmt);
  _warn_helper(THRIFT_LOG_INFO, NULL, fmt, ap);
  va_end(ap);
}

void TLogging::log_debug(const char *fmt, ...)
{
#ifdef _DEBUG
  va_list ap;

  va_start(ap, fmt);
  _warn_helper(THRIFT_LOG_DEBUG, NULL, fmt, ap);
  va_end(ap);
#endif
}

boost::function<void(int, const char*)> TLogging::logfunc_ = NULL;

void TLogging::setLogFunc(boost::function<void(int, const char*)> func)
{
  logfunc_ = func;
}

void TLogging::log(int severity, const char *msg)
{
  if (logfunc_) {
    logfunc_(severity, msg);
  }
  else {
    const char *severity_str;
    switch (severity) {
    case THRIFT_LOG_DEBUG:
      severity_str = "debug";
      break;
    case THRIFT_LOG_INFO:
      severity_str = "info";
      break;
    case THRIFT_LOG_WARN:
      severity_str = "warn";
      break;
    case THRIFT_LOG_ERR:
      severity_str = "error";
      break;
    default:
      severity_str = "???";
      break;
    }
    (void)fprintf(stderr, "[%s] %s\n", severity_str, msg);
  }
}

}
} // apache::thrift
